package com.audaque.springboot.foshanupload.web.testdemo.test;

import com.audaque.springboot.foshanupload.web.testdemo.model.domain.Person;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;

import java.time.Duration;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.*;
import static org.assertj.core.util.Lists.newArrayList;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

/**
 * @author zgb
 * @desc ...
 * @date 2023-09-02 01:29:06
 */
@DisplayName("显示名称测试")
public class Junit5est1 {

    //与junit4的@beforeClass类似，每个测试类运行一次
    @BeforeAll
    static void initAll() {
    }

    //与junit4中@before类似，每个测试用例都运行一次
    @BeforeEach
    void init() {
    }

    @Test
    @DisplayName("成功测试")
    void succeedingTest() {
    }

    @Test
    @DisplayName("失败测试")
    void failingTest() {
        org.junit.jupiter.api.Assertions.fail("a failing test");
    }

    //禁用测试用例
    @Test
    @Disabled("for demonstration purposes")
    void skippedTest() {
        // not executed
    }

    @Test
    @RepeatedTest(10)
    void abortedTest() {
        assumeTrue("abc".contains("Z"));
        org.junit.jupiter.api.Assertions.fail("test should have been aborted");
    }


    //与@BeforeEach对应，每个测试类执行一次，一般用于恢复环境
    @AfterEach
    void tearDown() {
    }

    //与@BeforeAll对应，每个测试类执行一次，一般用于恢复环境
    @AfterAll
    static void tearDownAll() {
    }

    @Test
    void groupedAssertions() {
        Person person = new Person();
        person.setName("niu");
        person.setAge(18);
        Assertions.assertAll("person",
                () -> assertEquals("niu", person.getName()),
                () -> assertEquals(18, person.getAge())
        );
    }

    @Test
    void exceptionTesting() {
        Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
            throw new IllegalArgumentException("a message");
        });
        assertEquals("a message", exception.getMessage());
    }

    @Test
    @DisplayName("超时测试")
    public void timeoutTest() {
        Assertions.assertTimeout(Duration.ofMillis(100), () -> Thread.sleep(50));
    }


    //通过标签把测试分组，在不同阶段执行不同的逻辑测试，比如划分为快速冒烟测试和执行慢但也重要的测试
    @Test
    @Tag("fast")
    void testing_faster() {
    }

    @Test
    @Tag("slow")
    void testing_slow() {
    }

    Stack<Object> stack;

    @Test
    @DisplayName("is instantiated with new Stack()")
    void isInstantiatedWithNew() {
        new Stack<>();
    }

    //@Nested 注解，能够以静态内部成员类的形式对测试用例类进行逻辑分组。
    @Nested
    @DisplayName("when new")
    class WhenNew {

        @BeforeEach
        void createNewStack() {
            stack = new Stack<>();
        }

        @Test
        @DisplayName("is empty")
        void isEmpty() {
            assertTrue(stack.isEmpty());
        }


        @Nested
        @DisplayName("after pushing an element")
        class AfterPushing {

            String anElement = "an element";

            @BeforeEach
            void pushAnElement() {
                stack.push(anElement);
            }

            @Test
            @DisplayName("it is no longer empty")
            void isNotEmpty() {
                assertFalse(stack.isEmpty());
            }
        }
    }

    //TestInfo可获取测试信息
    @Test
    @DisplayName("test-first")
    @Tag("my-tag")
    void test1(TestInfo testInfo) {
        assertEquals("test-first", testInfo.getDisplayName());
        assertTrue(testInfo.getTags().contains("my-tag"));
    }

    //TestReporter可以向控制台输出信息
    @Test
    @DisplayName("test-second")
    @Tag("my-tag")
    void test2(TestReporter testReporter) {
        testReporter.publishEntry("a key", "a value");
    }


    @BeforeEach
    @Timeout(5)
    void setUp() {
        // fails if execution time exceeds 5 seconds
    }

    @Test
    @Timeout(value = 1000, unit = TimeUnit.MILLISECONDS)
    void failsIfExecutionTimeExceeds1000Milliseconds() {
        // fails if execution time exceeds 1000 milliseconds
        //也可用这种方式 Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(1500));
    }


    @ParameterizedTest
    @ValueSource(strings = {"one", "two", "three"})
    @DisplayName("参数化测试1")
    public void parameterizedTest1(String string) {
        assertTrue(StringUtils.isNotBlank(string));
    }

    @ParameterizedTest
    @MethodSource("method")
    @DisplayName("方法来源参数")
    public void testWithExplicitLocalMethodSource(String name) {
        Assertions.assertNotNull(name);
    }

    private static Stream<String> method() {
        return Stream.of("apple", "banana");
    }

    @ParameterizedTest
    @CsvSource({"steven,18", "jack,24"})
    @DisplayName("参数化测试-csv格式")
    public void parameterizedTest3(String name, Integer age) {
        System.out.println("name:" + name + ",age:" + age);
        Assertions.assertNotNull(name);
        Assertions.assertTrue(age > 0);
    }

    @ParameterizedTest
    @CsvFileSource(resources = "/persons.csv")  //指定csv文件位置
    @DisplayName("参数化测试-csv文件")
    public void parameterizedTest2(String name, Integer age) {
        System.out.println("name:" + name + ",age:" + age);
        Assertions.assertNotNull(name);
        Assertions.assertTrue(age > 0);
    }


    @Test
    void testString() {
        // 断言null或为空字符串
        assertThat("").isNullOrEmpty();
        // 断言空字符串
        assertThat("").isEmpty();
        // 断言字符串相等 断言忽略大小写判断字符串相等
        assertThat("niu").isEqualTo("niu").isEqualToIgnoringCase("NIu");
        // 断言开始字符串 结束字符穿 字符串长度
        assertThat("niu").startsWith("ni").endsWith("u").hasSize(3);
        // 断言包含字符串 不包含字符串
        assertThat("niu").contains("iu").doesNotContain("love");
        // 断言字符串只出现过一次
        assertThat("niu").containsOnlyOnce("iu");
    }

    @Test
    void testNumber() {
        // 断言相等
        assertThat(42).isEqualTo(42);
        // 断言大于 大于等于
        assertThat(42).isGreaterThan(38).isGreaterThanOrEqualTo(38);
        // 断言小于 小于等于
        assertThat(42).isLessThan(58).isLessThanOrEqualTo(58);
        // 断言0
        assertThat(0).isZero();
        // 断言正数 非负数
        assertThat(1).isPositive().isNotNegative();
        // 断言负数 非正数
        assertThat(-1).isNegative().isNotPositive();
    }

    @Test
    void testCollection() {
        // 断言 列表是空的
        assertThat(newArrayList()).isEmpty();
        // 断言 列表的开始 结束元素
        assertThat(newArrayList(1, 2, 3)).startsWith(1).endsWith(3);
        // 断言 列表包含元素 并且是排序的
        assertThat(newArrayList(1, 2, 3)).contains(1, atIndex(0)).contains(2, atIndex(1)).contains(3)
                .isSorted();
        // 断言 被包含与给定列表
        assertThat(newArrayList(3, 1, 2)).isSubsetOf(newArrayList(1, 2, 3, 4));
        // 断言 存在唯一元素
        assertThat(newArrayList("a", "b", "c")).containsOnlyOnce("a");
    }

    @Test
    void testMap() {
        Map<String, Object> foo = ImmutableMap.of("A", 1, "B", 2, "C", 3);

        // 断言 map 不为空 size
        assertThat(foo).isNotEmpty().hasSize(3);
        // 断言 map 包含元素
        assertThat(foo).contains(entry("A", 1), entry("B", 2));
        // 断言 map 包含key
        assertThat(foo).containsKeys("A", "B", "C");
        // 断言 map 包含value
        assertThat(foo).containsValue(3);
    }


}
